#pragma once

#include <iostream>
#include <memory>
#include <sys/epoll.h>
#include <string>
#include <cerrno>
#include "log.hpp"
#include "Socket.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"

uint32_t EVENT_IN = (EPOLLIN);
uint32_t EVENT_OUT = (EPOLLOUT);

class EpollerServer : public nocopy
{
    static const int num = 64;

public:
    EpollerServer(uint16_t port)
        : _port(port),
          _listensocket_ptr(new Sock()),
          _Epoller_ptr(new Epoller())
    {
    }

    void Init()
    {

        _listensocket_ptr->Socket();
        int opt = 1;
        setsockopt(_listensocket_ptr->GetFd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        _listensocket_ptr->Bind(_port);
        _listensocket_ptr->Listen();

        lg(INFO, "crate socket, fd:%d ", _listensocket_ptr->GetFd());
    }

    void Accpeter()
    {
        std::string clientip;
        uint16_t clientport;
        int n = _listensocket_ptr->Accept(&clientip, &clientport);
        if (n > 0)
        {
            _Epoller_ptr->EpollUpdate(EPOLL_CTL_ADD, n, EVENT_IN);
            lg(INFO, "get a new link, client info@ %s:%d", clientip.c_str(), clientport);
        }
    }
    void recver(int fd)
    {
        char buffer[1024];
        int n = read(fd, &buffer, sizeof(buffer)-1);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
        }
        else if (n == 0)
        {
            close(fd);
            _Epoller_ptr->EpollUpdate(EPOLL_CTL_DEL,fd,0);
            lg(DEBUG, "socketfd quit...");
        }
        else
        {
            close(fd);
            _Epoller_ptr->EpollUpdate(EPOLL_CTL_DEL,fd,0);
            lg(ERROR, "read error,errno: %d , errstr: %s", errno, strerror(errno));
        }
    }

    void Dispatcher(struct epoll_event revs[], int num)
    {
        // 分配事件
        for (int i = 0; i < num; i++)
        {
            uint32_t event = revs[i].events;
            int fd = revs[i].data.fd;
            if (event & EVENT_IN)
            {
                if (fd == _listensocket_ptr->GetFd())
                {
                    Accpeter();
                }
                else
                {
                    recver(fd);
                }
            }
        }
    }

    void Start()
    {
        _Epoller_ptr->EpollUpdate(EPOLL_CTL_ADD, _listensocket_ptr->GetFd(), EVENT_IN);
        struct epoll_event revs[num];
        for (;;)
        {
            int n = _Epoller_ptr->EpollWait(revs, num);
            if (n > 0)
            {
                lg(DEBUG, "evnet happened, fd: %d", revs->data.fd);
                Dispatcher(revs, n);
            }
            else if (n == 0)
            {
                lg(INFO, "time out ...");
            }
            else
            {
                lg(ERROR, "epll wait error");
            }
        }
    }

    ~EpollerServer()
    {
    }

private:
    Sock s;
    std::shared_ptr<Sock> _listensocket_ptr;
    std::shared_ptr<Epoller> _Epoller_ptr;
    uint16_t _port;
};